#include "include/cxxstd.h"
#include "include/config.h"

// std <([{
namespace std
{
type_info::type_info(const char* __n)
{
	__name = __n;
}

type_info::~type_info()
{
}

bool type_info::upCast(const type_info* thr_type) const
{
	return *this == *thr_type;
}

bad_cast::bad_cast() throw()
{
}

bad_cast::~bad_cast() throw()
{
}

const char* bad_cast::what() const throw()
{
	return "std::bad_cast";
}

EXPORT_CXXDATA(_ZTVSt8bad_cast);

bad_typeid::bad_typeid() throw()
{
}

bad_typeid::~bad_typeid() throw()
{
}

const char* bad_typeid::what() const throw()
{
	return "std::bad_typeid";
}

EXPORT_CXXDATA(_ZTVSt10bad_typeid);
}
// }])>

// __cxxabiv1 <([{
namespace __cxxabiv1
{
class __class_type_info;

template <typename T> inline const T* adjustPointer(const void* base, ptrdiff_t offset)
{
	return reinterpret_cast<const T*>(reinterpret_cast<const ptr_t>(base) + offset);
}

struct VtablePrefix
{
	ptrdiff_t wholeOffset;
	const __class_type_info *wholeType;
	const void *origin;
};

// class hierarchy:
// __si_class_type_info, __vmi_class_type_info << __class_type_info << std::type_info.

// __class_type_info <([{
class __class_type_info : public std::type_info
{
	public:
		explicit __class_type_info(const char *__n) : type_info(__n) {}
		virtual ~__class_type_info() {}

		virtual bool upCast(const type_info* exType) const;
		virtual const void* dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const;
};

bool __class_type_info::upCast(const type_info* exType) const
{
	return *this == *exType;
}

const void* __class_type_info::dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const
{
	if (*this == *destType)
		return adjustPtr;
	if (*this == *srcType && adjustPtr == srcPtr)
		// dynamic_cast operator can't be used to upcast pointer.
		return NULL;
	return NULL;
}
EXPORT_CXXDATA(_ZTVN10__cxxabiv117__class_type_infoE);
// }])>

// __si_class_type_info <([{
class __si_class_type_info : public __class_type_info
{
	public:
		const __class_type_info* baseType;

		explicit __si_class_type_info(const char *__n, const __class_type_info *__base) : __class_type_info(__n), baseType(__base) {}
		virtual ~__si_class_type_info() {}

		virtual bool upCast(const type_info* exType) const;
		virtual const void* dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const;
};

bool __si_class_type_info::upCast(const type_info* exType) const
{
	return __class_type_info::upCast(exType) || baseType->upCast(exType);
}

const void* __si_class_type_info::dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const
{
	const void* result = __class_type_info::dynCast(destType, srcType, srcPtr, adjustPtr);
	if (result == NULL)
		result = baseType->dynCast(destType, srcType, srcPtr, adjustPtr);
	return result;
}
EXPORT_CXXDATA(_ZTVN10__cxxabiv120__si_class_type_infoE);
// }])>

// __vmi_class_type_info <([{
class __vmi_class_type_info : public __class_type_info
{
	public:
		unsigned int 		flags;
		unsigned int 		count;
		struct {
			const __class_type_info* type;

			long isVirtual : 1;
			long isPublic : 1;
			long unused : 6;
			long offset : 24;
		} data[0];

		explicit __vmi_class_type_info(const char* __n, int __flags) : __class_type_info(__n), flags(__flags), count(0) { }
		virtual ~__vmi_class_type_info() { }

		virtual bool upCast(const type_info* exType) const;
		virtual const void* dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const;
};
bool __vmi_class_type_info::upCast(const type_info* exType) const
{
	return __class_type_info::upCast(exType) || data[0].type->upCast(exType);
}

const void* __vmi_class_type_info::dynCast(const __class_type_info* destType, const __class_type_info* srcType, const void* srcPtr, const void* adjustPtr) const
{
	const void* result = __class_type_info::dynCast(destType, srcType, srcPtr, adjustPtr);
	if (result != NULL)
		goto done;
	for (std::size_t i = count; i--; ) { // Be careful! iterates from count to 1.
		ptrdiff_t offset = data[i].offset;
		const void* base = adjustPtr;
		if (data[i].isVirtual) {
			const void* vtable = *static_cast<const void *const *>(base);
			offset = *adjustPointer<ptrdiff_t>(vtable, offset);
		}
		base = adjustPointer<void>(base, offset);
		result = data[i].type->dynCast(destType, srcType, srcPtr, base);
		if (result != NULL)
			goto done;
	}
done:
	return result;
}
EXPORT_CXXDATA(_ZTVN10__cxxabiv121__vmi_class_type_infoE);
// }])>

extern "C" void* __dynamic_cast(const void* srcPtr,
		const __class_type_info* srcType,
		const __class_type_info* dstType,
		ptrdiff_t)
{
	const void* vtable = *static_cast<const void* const*>(srcPtr);
	const VtablePrefix* prefix = adjustPointer<VtablePrefix>(vtable, -offsetof(VtablePrefix, origin));
	const void* wholePtr = adjustPointer<void>(srcPtr, prefix->wholeOffset);
	const __class_type_info *wholeType = prefix->wholeType;

	return const_cast<void*>(wholeType->dynCast(dstType, srcType, srcPtr, wholePtr));
}
EXPORT_CSYMBOL(__dynamic_cast);
}
// }])>
// vim: foldmarker=<([{,}])> foldmethod=marker
